#!/usr/bin/env bash
#
# Run the include-cleaner tool (iwyu replacement) on a file in the webrtc source
# directory.
#
#
# In order to handle include paths correctly, you need to provide
# a compile DB (aka compile_commands.json).
# You can create it in one of the following ways:
# "gn gen --export-compile-commands path/to/out"
# "tools/clang/scripts/generate_compdb.py -p path/to/out > compile_commands.json"
# If "out/Default" exists, the script will attempt to generate it for you.
#
# clang-include-cleaner is built as part of the "clangd" package in our
# LLVM build.
# Example .gclient file:
# solutions = [
#  {
#    "name": "src",
#    "url": "https://webrtc.googlesource.com/src.git",
#    "deps_file": "DEPS",
#    "managed": False,
#    "custom_deps": {},
#    "custom_vars" : {
#      "checkout_clangd": True,
#      "download_remoteexec_cfg" : True,
#    }
#  },
# ]


CLEANER=third_party/llvm-build/Release+Asserts/bin/clang-include-cleaner
if [ ! -x $CLEANER ]; then
  echo "clang-include-cleaner not found"
  echo -n "Add '\"checkout_clangd\": True' to 'custom_vars' in your"
  echo ".gclient file and run 'gclient sync'."
  exit 1
fi

# Debug level, also controlled by the "-d" argument.
# Set this to 1 to get more debug information.
# Set this to 2 to also get a dump of the iwyu tool output.
DEBUG=0

set -e
if [ $DEBUG -gt 0 ]; then
  set -x
fi

error() {
  echo "$*" >&2
  exit 1
}

WORKDIR=out/Default

usage() {
  echo "Usage: $0 [-r] file.cc [file2.cc ...]"
  echo "Runs the include-cleaner tool on a list of files"
  echo "Arguments:"
  echo " -n : Just print changes, don't do them"
  echo " -c : Just return non-zero exit code if there are changes, don't do them"
  echo " -r : Remove non-required includes from .h file"
  echo " -d <n> : Set debug level to <n>"
  echo " -w : Specify the workdir (out/Default if not specified)"
  echo " -h : Print this help message"
}

COMMAND=" --edit"
INCLUDE_ARGS=""
GMOCK_INCLUDES="--extra-arg=-I../../third_party/googletest/src/googlemock/include/"
GTEST_INCLUDES="--extra-arg=-I../../third_party/googletest/src/googletest/include/"
CHECK_MODE=false

while getopts 'd:rncw:h' opts; do
  case "${opts}" in
    n) COMMAND=" --print=changes" ;;
    c) COMMAND=" --print=changes" ; CHECK_MODE=true ;;
    r) INCLUDE_ARGS=" --remove" ;;
    d) DEBUG=${OPTARG};if [ $DEBUG -gt 0 ]; then set -x; fi  ;;
    w) WORKDIR=${OPTARG} ;;
    h) usage; exit 1 ;;
    *) error "Unexpected option ${opts}" ;;
  esac
done
shift $(expr $OPTIND - 1 )

if [[ -z "$COMPILE_COMMANDS" ]]; then
  if [ -d "$WORKDIR" ]; then
    if [ ! -f "$WORKDIR/compile_commands.json" ]; then
      echo "Generating compile commands file"
      tools/clang/scripts/generate_compdb.py -p $WORKDIR > $WORKDIR/compile_commands.json
    fi
    COMPILE_COMMANDS="$WORKDIR/compile_commands.json"
  else
    error "Could not generate $WORKDIR/compile_commands.json."
  fi
fi

# To get a list of files in a commit: git diff-tree --no-commit-id --name-only -r HEAD
for FILE in "$@"
do
  if [ -z $FILE ] || [ ! -f $FILE ]; then
    usage
    error "File $FILE is not found"
  fi
done

HAS_OUTPUT=false
for FILE in "$@"
do
  OUTPUT=$($CLEANER -p $WORKDIR $INCLUDE_ARGS $GMOCK_INCLUDES $GTEST_INCLUDES $COMMAND $FILE)

  # include-cleaner does not support custom mappings for certain deps
  # this ensures that the gtest/gmock deps it inserts are replaced
  # with the right paths for those includes.
  # Since sed inplace argument acts differently between GNU/BSD based systems
  # we handle this here.
  case "$(uname -s)" in
      Linux*)     INPLACE_ARG=( -i );;
      Darwin*)    INPLACE_ARG=( -i '' );;
      *)          INPLACE_ARG=( -i )
  esac
  IWYU_MAPPING=( "\"gmock\/gmock\.h\":\"test\/gmock\.h\""
                 "\"gtest\/gtest\.h\":\"test\/gtest\.h\""
                 "\<sys\/socket\.h\>:\"rtc_base\/net_helpers\.h\"" )

  for mapping in "${IWYU_MAPPING[@]}" ; do
    KEY="${mapping%%:*}"
    VALUE="${mapping##*:}"
    if grep -q "#include ${VALUE}" $FILE; then
      OUTPUT=$(echo "$OUTPUT" | sed "/+ ${KEY}/d")
      sed "${INPLACE_ARG[@]}" -e "/#include ${KEY}/d" $FILE
    else
      sed "${INPLACE_ARG[@]}" -e "s@^#include ${KEY}@#include ${VALUE}@g" $FILE
    fi
  done

  echo "${OUTPUT}"
  HAS_OUTPUT=$HAS_OUTPUT || [[ ! -z $OUTPUT ]]
done

echo "Finished. Check diff, compile, gn gen --check (tools_webrtc/gn_check_autofix.py can fix most of the issues)"
echo "and git cl format before uploading."

# Return a non-zero exit code if running with "CHECK_MODE"
# and there are changes to apply.
if $CHECK_MODE && [[ ! -z $OUTPUT ]]; then
  exit 1
fi
